import serial import serial.tools.list_ports import threading import asyncio import json import time from fastapi import FastAPI, WebSocket, WebSocketDisconnect from fastapi.responses import FileResponse app = FastAPI() latest = {} lock = threading.Lock() clients = [] def find_port(): for p in serial.tools.list_ports.comports(): if any(x in p.description.upper() for x in ["USB", "UART", "CP210", "CH340"]): return p.device return None async def broadcast(msg): for ws in clients[:]: try: await ws.send_text(msg) except: clients.remove(ws) def serial_reader(): ser = serial.Serial(find_port(), 115200, timeout=1) time.sleep(2) while True: try: parts = ser.readline().decode('utf-8').strip().split(',') if len(parts) != 6: continue data = {k: float(v) for k, v in zip(["ax","ay","az","gx","gy","gz"], parts)} with lock: latest.update(data) asyncio.run(broadcast(json.dumps(data))) except: pass @app.get("/status") def status(): with lock: return {"connected": bool(latest), "latest": latest} @app.websocket("/ws") async def ws_endpoint(ws: WebSocket): await ws.accept() clients.append(ws) try: while True: await ws.receive_text() except WebSocketDisconnect: clients.remove(ws) @app.on_event("startup") def startup(): threading.Thread(target=serial_reader, daemon=True).start() @app.get("/") def dashboard(): return FileResponse("dashboard.html")